/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// gl_mesh.c: triangle model functions
// drawing
#include "quakedef.h"

class meshbuffer_t
{
public:
	meshbuffer_t (void)
	{
		// clear everything
		this->ClearInfo ();
	}

	void Release (void)
	{
		SAFE_RELEASE (this->Positions);
		SAFE_RELEASE (this->TexCoords);
		SAFE_RELEASE (this->Indexes);
		SAFE_RELEASE (this->AdjacencyVertexes);
		SAFE_RELEASE (this->AdjacencyIndexes);
		this->ClearInfo ();
	}

	BOOL Exists (void)
	{
		// if any component is missing the entire buffer set is missing
		if (!this->Name) return FALSE;
		if (!this->Positions) return FALSE;
		if (!this->TexCoords) return FALSE;
		if (!this->Indexes) return FALSE;
		if (!this->AdjacencyVertexes) return FALSE;
		if (!this->AdjacencyIndexes) return FALSE;

		// the set is good now
		return TRUE;
	}

	BOOL Matches (model_t *m, aliashdr_t *hdr)
	{
		// if anything is different the entire set is different
		if (strcmp (this->Name, m->name)) return FALSE;
		if (this->NumFrames != hdr->nummeshframes) return FALSE;
		if (this->NumVerts != hdr->numverts) return FALSE;
		if (this->NumIndexes != hdr->numindexes) return FALSE;

		// the set matches now
		return TRUE;
	}

	void ClearInfo (void)
	{
		if (this->Name)
		{
			zoneHeap->Free (this->Name);
			this->Name = NULL;
		}

		this->NumFrames = 0;
		this->NumVerts = 0;
		this->NumIndexes = 0;
	}

	void SetInfo (model_t *m, aliashdr_t *hdr)
	{
		if (this->Name)
		{
			zoneHeap->Free (this->Name);
			this->Name = NULL;
		}

		this->Name = CopyString (m->name);
		this->NumFrames = hdr->nummeshframes;
		this->NumVerts = hdr->numverts;
		this->NumIndexes = hdr->numindexes;
	}

	void Load (model_t *m, aliashdr_t *hdr)
	{
		// trash all existing buffers
		this->Release ();

		// create a buffer set
		this->LoadPositions (hdr);
		this->LoadTexCoords (hdr);
		this->LoadIndexes (hdr);
		this->LoadAdjacency (hdr);

		// mark it as used in this sequence
		this->Register ();

		// fill in properties
		this->SetInfo (m, hdr);
	}

	void Register (void)
	{
		// mark it as used in this sequence
		this->RegistrationSequence = r_RegistrationSequence;
	}

	void CheckRegistration (void)
	{
		// allow to persist for 2 map changes
		if (this->RegistrationSequence >= (r_RegistrationSequence - 1)) return;

		// trash all existing buffers
		this->Release ();
	}

	void SetupAdjacency (aliashdr_t *hdr, int prevpose, int currpose)
	{
		ID3D10Buffer *Buffers[2] = {this->AdjacencyVertexes, this->AdjacencyVertexes};
		UINT strides[2] = {sizeof (adjvert_t), sizeof (adjvert_t)};
		UINT offsets[2] = {prevpose * (hdr->vertsperframe + 1) * sizeof (adjvert_t), currpose * (hdr->vertsperframe + 1) * sizeof (adjvert_t)};

		d3d_Device->IASetPrimitiveTopology (D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ);
		d3d_Device->IASetVertexBuffers (0, 2, Buffers, strides, offsets);
		d3d_Device->IASetIndexBuffer (this->AdjacencyIndexes, DXGI_FORMAT_R16_UINT, 0);
	}

	void SetupToDraw (aliashdr_t *hdr, int prevpose, int currpose)
	{
		ID3D10Buffer *Buffers[3] = {this->Positions, this->Positions, this->TexCoords};
		UINT strides[3] = {sizeof (aliaspolyvert_t), sizeof (aliaspolyvert_t), sizeof (D3DXFLOAT16) * 2};
		UINT offsets[3] = {prevpose * hdr->numverts * sizeof (aliaspolyvert_t), currpose * hdr->numverts * sizeof (aliaspolyvert_t), 0};

		d3d_Device->IASetPrimitiveTopology (D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		d3d_Device->IASetVertexBuffers (0, 3, Buffers, strides, offsets);
		d3d_Device->IASetIndexBuffer (this->Indexes, DXGI_FORMAT_R16_UINT, 0);
	}

private:
	void LoadAdjacency (aliashdr_t *hdr)
	{
		adjvert_t *verts = NULL;
		QSCRATCHLOCKER Lock ((void **) &verts, __FUNCTION__);
		D3D10_SUBRESOURCE_DATA vbSRD = {verts, 0, 0};
		D3D10_SUBRESOURCE_DATA ibSRD = {hdr->adjacency, 0, 0};

		for (int posenum = 0; posenum < hdr->nummeshframes; posenum++)
		{
			trivertx_t *vert = hdr->frameverts[posenum];

			// thse are copied in in order
			for (int i = 0; i < hdr->vertsperframe; i++, verts++)
			{
				verts->xyz[0] = (float) vert[i].v[0] * hdr->scale[0] + hdr->scale_origin[0];
				verts->xyz[1] = (float) vert[i].v[1] * hdr->scale[1] + hdr->scale_origin[1];
				verts->xyz[2] = (float) vert[i].v[2] * hdr->scale[2] + hdr->scale_origin[2];
				verts->xyz[3] = 1.0f;
			}

			// add an extra vert at the end of each frame for the "bad neighbour" case
			verts->xyz[0] = 20;
			verts->xyz[1] = 10;
			verts->xyz[2] = 5;
			verts->xyz[3] = 0;
			verts++;
		}

		D3D10_BUFFER_DESC vbDesc = {
			hdr->nummeshframes * (hdr->vertsperframe + 1) * sizeof (adjvert_t),
			D3D10_USAGE_IMMUTABLE,
			D3D10_BIND_VERTEX_BUFFER,
			0,
			0
		};

		d3d_Device->CreateBuffer (&vbDesc, &vbSRD, &this->AdjacencyVertexes);

		D3D10_BUFFER_DESC ibDesc = {
			hdr->numadjacency * sizeof (unsigned short),
			D3D10_USAGE_IMMUTABLE,
			D3D10_BIND_INDEX_BUFFER,
			0,
			0
		};

		d3d_Device->CreateBuffer (&ibDesc, &ibSRD, &this->AdjacencyIndexes);
	}

	void LoadPositions (aliashdr_t *hdr)
	{
		aliaspolyvert_t *polyverts = NULL;
		QSCRATCHLOCKER Lock ((void **) &polyverts, __FUNCTION__);
		D3D10_SUBRESOURCE_DATA srd = {polyverts, 0, 0};

		// take space at the end of the polyverts buffer for normals fixup
		vertexnormals_t *vnorms = (vertexnormals_t *) &polyverts[hdr->nummeshframes * hdr->numverts];

		Vector3Set (hdr->mids, 0, 0, 0);

		for (int posenum = 0; posenum < hdr->nummeshframes; posenum++)
		{
			trivertx_t *vert = hdr->frameverts[posenum];

			// copy scaled position first so that we can derive normals from it
			for (int i = 0; i < hdr->numverts; i++)
			{
				polyverts[i].xyz[0] = (float) vert[hdr->meshverts[i].vertindex].v[0] * hdr->scale[0] + hdr->scale_origin[0];
				polyverts[i].xyz[1] = (float) vert[hdr->meshverts[i].vertindex].v[1] * hdr->scale[1] + hdr->scale_origin[1];
				polyverts[i].xyz[2] = (float) vert[hdr->meshverts[i].vertindex].v[2] * hdr->scale[2] + hdr->scale_origin[2];

				Vector3Add (hdr->mids, hdr->mids, polyverts[i].xyz);
			}

			Mod_FixupAliasNormals (hdr, vnorms, polyverts);

			Vector3Scale (hdr->mids, 1.0f / (float) (hdr->nummeshframes * hdr->numverts), hdr->mids);
			polyverts += hdr->numverts;
		}

		// and create the buffer
		D3D10_BUFFER_DESC vbDesc = {
			hdr->nummeshframes * hdr->numverts * sizeof (aliaspolyvert_t),
			D3D10_USAGE_IMMUTABLE,
			D3D10_BIND_VERTEX_BUFFER,
			0,
			0
		};

		d3d_Device->CreateBuffer (&vbDesc, &srd, &this->Positions);
	}

	void LoadTexCoords (aliashdr_t *hdr)
	{
		D3DXFLOAT16 *st = NULL;
		QSCRATCHLOCKER Lock ((void **) &st, __FUNCTION__);
		D3D10_SUBRESOURCE_DATA srd = {st, 0, 0};

		for (int i = 0; i < hdr->numverts; i++, st += 2)
		{
			st[0] = hdr->meshverts[i].st[0];
			st[1] = hdr->meshverts[i].st[1];
		}

		D3D10_BUFFER_DESC vbDesc = {
			hdr->numverts * sizeof (D3DXFLOAT16) * 2,
			D3D10_USAGE_IMMUTABLE,
			D3D10_BIND_VERTEX_BUFFER,
			0,
			0
		};

		d3d_Device->CreateBuffer (&vbDesc, &srd, &this->TexCoords);
	}

	void LoadIndexes (aliashdr_t *hdr)
	{
		D3D10_BUFFER_DESC ibDesc = {
			hdr->numindexes * sizeof (unsigned short),
			D3D10_USAGE_IMMUTABLE,
			D3D10_BIND_INDEX_BUFFER,
			0,
			0
		};

		D3D10_SUBRESOURCE_DATA srd = {hdr->indexes, 0, 0};

		d3d_Device->CreateBuffer (&ibDesc, &srd, &this->Indexes);
	}

	ID3D10Buffer *Positions;
	ID3D10Buffer *TexCoords;
	ID3D10Buffer *Indexes;
	ID3D10Buffer *AdjacencyVertexes;
	ID3D10Buffer *AdjacencyIndexes;

	char *Name;
	int NumFrames;
	int NumVerts;
	int NumIndexes;
	int RegistrationSequence;
};


QSHADER d3d_MeshNormalShader;
QSHADER d3d_MeshLumaShader;
QSHADER d3d_MeshZFill;
QSHADER d3d_MeshDeferred;

meshbuffer_t d3d_MeshBuffers[MAX_MODELS];


void Mesh_ShutdownBuffers (void)
{
	for (int i = 0; i < MAX_MODELS; i++)
		d3d_MeshBuffers[i].Release ();
}


class CMeshHandler : public CD3DHandler
{
public:
	void OnCreateDevice (void)
	{
		d3d_MeshNormalShader.CreateShaders (IDR_MESHSHADER, "MeshVS", NULL, "MeshPS", INPUT_LAYOUT_MESH);
		d3d_MeshLumaShader.CreateShaders (IDR_MESHSHADER, "MeshVS", NULL, "MeshLumaPS", INPUT_LAYOUT_MESH);
		d3d_MeshZFill.CreateShaders (IDR_MESHSHADER, "MeshZFillVS", NULL, NULL, INPUT_LAYOUT_MESH);
		d3d_MeshDeferred.CreateShaders (IDR_LIGHT, "MeshDeferredVS", NULL, "SurfDeferredPS", INPUT_LAYOUT_MESH);
	}

	void OnReleaseDevice (void)
	{
		for (int i = 0; i < MAX_MODELS; i++)
			d3d_MeshBuffers[i].Release ();
	}
} d3d_MeshHandler;


void R_GetMeshBufferSet (model_t *m, aliashdr_t *hdr)
{
	int FreeSlot = -1;

	for (int i = 0; i < MAX_MODELS; i++)
	{
		// mark a free slot
		if (!d3d_MeshBuffers[i].Exists ())
		{
			FreeSlot = i;
			continue;
		}

		// check if the set matches
		if (!d3d_MeshBuffers[i].Matches (m, hdr)) continue;

		// this is a matching set so mark it as active in this registration sequence and return it
		d3d_MeshBuffers[i].Register ();
		m->BufferSetNum = i;
		return;
	}

	if (FreeSlot == -1)
	{
		Sys_Error ("R_GetMeshBufferSet : out of buffers!");
		return;
	}

	// take this slot
	m->BufferSetNum = FreeSlot;

	// and load the buffer set
	d3d_MeshBuffers[m->BufferSetNum].Load (m, hdr);
}


void R_LoadMeshBuffers (void)
{
	model_t	*m;

	for (int j = 1; j < MAX_MODELS; j++)
	{
		if (!(m = cl.model_precache[j])) continue;

		// don't crash on models with 0 surfaces attempting to create a vertex buffer
		if (m->type != mod_alias) continue;

		// and create the buffers
		R_GetMeshBufferSet (m, m->aliashdr);
	}

	// trash all buffers not used in this registration sequence
	for (int j = 0; j < MAX_MODELS; j++)
	{
		d3d_MeshBuffers[j].CheckRegistration ();
	}
}


float R_CalcLerpBlend (entity_t *e, double lerpstart)
{
	float blend;

	if (e->lerpflags & LERP_FINISH)
		blend = (cl.time - lerpstart) / (e->lerpfinish - lerpstart);
	else blend = (cl.time - lerpstart) / 0.1;

	if (blend < 0.0f) return 0.0f;
	if (blend > 1.0f) return 1.0f;

	return blend;
}


void R_MinimumLight (float *light, float minlight)
{
	// convert the colour space, boost luminance, then convert back to maintain original colour balance
	RGBToYCoCg (light);
	light[0] = (light[0] + minlight) / (1.0f + minlight);
	YCoCgToRGB (light);
}


void R_ClampAliasLight (entity_t *e, float *light)
{
	if (e == &cl.viewent) R_MinimumLight (light, 0.5f);
	if (e->entitynum >= 1 && e->entitynum <= cl.maxclients) R_MinimumLight (light, 0.25f);

	if (e->model->flags & EF_ROTATE)
	{
		VectorScale (light, 1.0f + 0.25f * sin (cl.time * 7.0f), light);
		R_MinimumLight (light, 0.25f);
	}
}


void R_LightAliasModel (entity_t *e, aliashdr_t *hdr, float alpha = 1.0f)
{
	// get lighting information
	if (!(e->effects & EF_STATICENT))
	{
		R_LightPoint (e->lerporigin, &e->lightpoint);

		float an = ((e->lerpangles[0] + e->lerpangles[1]) / 180) * M_PI;
		e->shadevector[0] = cos (-an);
		e->shadevector[1] = sin (-an);
		e->shadevector[2] = 1;
		VectorNormalize (e->shadevector);
	}

	// convert the lightpoint to a usable colour
	R_LightPointToShadeLight (&e->lightpoint, e->shadelight);

	// and clamp it
	R_ClampAliasLight (e, e->shadelight);

	// take it down for a consistent range (this is quite approximate)
	VectorScale (e->shadelight, 0.566f, e->shadelight);
}


void R_SetupMeshFrame (entity_t *e, aliashdr_t *hdr, int frame)
{
	int	posenum, numposes;
	float frame_interval;

	if ((frame >= hdr->numframes) || (frame < 0))
	{
		Con_Printf (PRINT_DEVELOPER, "R_AliasSetupFrame: no such frame %d\n", frame);
		frame = 0;
	}

	posenum = hdr->frames[frame].firstpose;
	numposes = hdr->frames[frame].numposes;

	if (numposes > 1)
	{
		// to do - run through Mod_GetAutoAnimation
		frame_interval = hdr->frames[frame].interval;
		posenum += (int) (cl.time / frame_interval) % numposes;
	}
	else frame_interval = 0.1f;

	if (e->lerpflags & LERP_RESETANIM)
	{
		// kill any lerp in progress
		e->lerpstart = 0;
		e->prevpose = posenum;
		e->currpose = posenum;
		e->lerpflags &= ~LERP_RESETANIM;
	}
	else if (e->currpose != posenum)
	{
		// pose changed, start new lerp
		if (e->lerpflags & LERP_RESETANIM2)
		{
			// defer lerping one more time
			e->lerpstart = 0;
			e->prevpose = posenum;
			e->currpose = posenum;
			e->lerpflags &= ~LERP_RESETANIM2;
		}
		else
		{
			e->lerpstart = cl.time;
			e->prevpose = e->currpose;
			e->currpose = posenum;
		}
	}

	if (e->currpose != e->prevpose)
	{
		// needs to be deferred to here because the above can modify e->lerpstart
		if (e->lerpflags & LERP_FINISH)
			e->poseblend = (cl.time - e->lerpstart) / (e->lerpfinish - e->lerpstart);
		else e->poseblend = (cl.time - e->lerpstart) / frame_interval;

		// and clamp it
		if (e->poseblend < 0.0f) e->poseblend = 0.0f;
		if (e->poseblend > 1.0f) e->poseblend = 1.0f;
	}
	else e->poseblend = 0.0f;
}


void R_LerpEntityTransform (entity_t *e, int axis, BOOL steplerp)
{
	float blend;

	if (e->lerpflags & LERP_RESETMOVE)
	{
		e->originlerpstart[axis] = e->angleslerpstart[axis] = 0;
		e->prevorigin[axis] = e->currorigin[axis] = e->origin[axis];
		e->prevangles[axis] = e->currangles[axis] = e->angles[axis];
	}
	else
	{
		if (e->origin[axis] != e->currorigin[axis])
		{
			e->originlerpstart[axis] = cl.time;
			e->prevorigin[axis] = e->currorigin[axis];
			e->currorigin[axis] = e->origin[axis];
		}

		if (e->angles[axis] != e->currangles[axis])
		{
			e->angleslerpstart[axis] = cl.time;
			e->prevangles[axis] = e->currangles[axis];
			e->currangles[axis] = e->angles[axis];
		}
	}

	if (steplerp)
	{
		blend = R_CalcLerpBlend (e, e->originlerpstart[axis]);
		e->lerporigin[axis] = e->prevorigin[axis] + (e->currorigin[axis] - e->prevorigin[axis]) * blend;
	}
	else e->lerporigin[axis] = e->origin[axis];

	blend = R_CalcLerpBlend (e, e->angleslerpstart[axis]);
	e->lerpangles[axis] = e->prevangles[axis] + RAD2DEG (asin (sin (DEG2RAD (e->currangles[axis] - e->prevangles[axis])) * blend));
}


void R_SetupEntityTransform (entity_t *e)
{
	if (e != &cl.viewent && (e->lerpflags & LERP_MOVESTEP))
	{
		// lerp around each axis separately
		R_LerpEntityTransform (e, 0, TRUE);
		R_LerpEntityTransform (e, 1, TRUE);
		R_LerpEntityTransform (e, 2, e->lerpflags & LERP_STEPZ);
	}
	else
	{
		Vector3Copy (e->lerporigin, e->origin);
		Vector3Copy (e->lerpangles, e->angles);
	}

	// clear the move reset bit if it was set
	e->lerpflags &= ~LERP_RESETMOVE;
}


QTEXTURE *R_GetMeshTexture (entity_t *e, aliashdr_t *hdr)
{
	if (r_lightmap->value)
		return &d3d_WhiteTexture;
	else
	{
		QTEXTURE *Texture = NULL;

		if (hdr->skins[e->skinnum].type == ALIAS_SKIN_SINGLE)
			Texture = hdr->skins[e->skinnum].skins->Texture[0];
		else
		{
			int anim = Mod_GetAutoAnimation (hdr->skins[e->skinnum].intervals, hdr->skins[e->skinnum].numintervals, e->syncbase);
			Texture = hdr->skins[e->skinnum].skins[anim].Texture[0];
		}

		// we can't dynamically colormap textures, so they are cached seperately for the players.  Heads are just uncolored.
		if (e->colormap != vid.colormap && !gl_nocolors->value)
		{
			int i = e->entitynum;

			// fixme - is this the correct head texture???
			if (i >= 1 && i <= cl.maxclients && PlayerTextures[-1 + i].Texture) Texture = PlayerTextures[-1 + i].Texture;
		}

		return Texture;
	}
}


QTEXTURE *R_GetMeshLuma (entity_t *e, aliashdr_t *hdr)
{
	if (hdr->skins[e->skinnum].type == ALIAS_SKIN_SINGLE)
		return hdr->skins[e->skinnum].skins->Texture[1];
	else
	{
		int anim = Mod_GetAutoAnimation (hdr->skins[e->skinnum].intervals, hdr->skins[e->skinnum].numintervals, e->syncbase);
		return hdr->skins[e->skinnum].skins[anim].Texture[1];
	}
}


BOOL R_SetupAliasModel (entity_t *e)
{
	aliashdr_t *hdr = e->model->aliashdr;
	bbox_t *bbox = &e->bbox;

	// skip over the transform setup for statics
	if (!(e->effects & EF_STATICENT))
	{
		// get the transform in local space first so that we can correctly do aabb->obb->aabb stuff
		R_SetupEntityTransform (e);

		// and now transform by the lerped origin and angles
		if (e->lerpangles[0] || e->lerpangles[1] || e->lerpangles[2])
		{
			e->localmatrix.Identity ();
			e->localmatrix.Translate (e->lerporigin[0], e->lerporigin[1], e->lerporigin[2]);
			e->localmatrix.Rotate (-e->lerpangles[0], e->lerpangles[1], e->lerpangles[2]);
		}
		else
		{
			e->localmatrix.Load (
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				e->lerporigin[0], e->lerporigin[1], e->lerporigin[2], 1
			);
		}

		// double size of eyes, since they are really hard to see in gl
		if (e->model->flags & EF_EYES)
		{
			float eyescale = gl_doubleeyes->value + 1.0f;

			if (eyescale > 0)
			{
				float ofs[3] = {hdr->mids[0] * gl_doubleeyes->value, hdr->mids[1] * gl_doubleeyes->value, hdr->mids[2] * gl_doubleeyes->value};

				e->localmatrix.Mult (
					eyescale, 0, 0, 0,
					0, eyescale, 0, 0,
					0, 0, eyescale, 0,
					-ofs[0], -ofs[1], -ofs[2], 1
				);
			}
		}
	}

	// setup poses/etc (also done early so that we can extract the correct bbox for the current poses)
	R_SetupMeshFrame (e, hdr, e->frame);

	// compute the bounding box
	Mod_ComputeAliasBBox (e, hdr, bbox->mins, bbox->maxs);
	Mod_GetBBoxCenter (bbox->mids, bbox->mins, bbox->maxs);

	// cull is left to here as positioning and scaling will affect it
	if (e != &cl.viewent)
	{
		// and now do the culling test
		if (R_CullBox (ref.Frustum, bbox->mins, bbox->maxs, 15))
		{
			// the model is not drawn
			e->lerpflags |= LERP_RESETALL;
			return FALSE;
		}
	}

	// the model is to be drawn
	c_alias_polys += hdr->numtris;
	return TRUE;
}


void R_RenderAliasModel (entity_t *e, aliashdr_t *hdr, QSHADER *shader, ID3D10BlendState *blendmode, ID3D10DepthStencilState *depthmode)
{
	// check for info that only needs to be updated once in multipass drawing
	if (e->PassNum != ref.AliasPasses)
	{
		// setup the cbuffer for this pass
		R_SetupObjectConstants (e, 1.0f);

		// common vertex setup
		d3d_MeshBuffers[e->model->BufferSetNum].SetupToDraw (hdr, e->prevpose, e->currpose);
	}

	d3d_State->OMSetDepthState (depthmode);
	d3d_State->OMSetBlendState (blendmode);

	// bind the shaders used by this pass
	shader->Bind ();

	// and draw the pass
	d3d_Device->DrawIndexed (hdr->numindexes, 0, 0);
	c_draw_call++;
	e->PassNum = ref.AliasPasses;
}


void dlight_t::DrawMesh (entity_t *ent, aliashdr_t *hdr, QSHADER *shader)
{
	// need to re-bind the texture here otherwise it won't draw...!
	if (ent->PassNum != ref.AliasPasses)
		d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &R_GetMeshTexture (ent, hdr)->SRV);

	R_RenderAliasModel (ent, hdr, shader, d3d_BlendAdditive, d3d_ZmodeNowrite);
	ent->PassNum = ref.AliasPasses;
}


void R_DrawAliasModel (entity_t *e)
{
	model_t *clmodel = e->model;
	aliashdr_t *hdr = clmodel->aliashdr;

	// signifies that we're going to a new mesh
	ref.AliasPasses++;

	// move the eye position back to the entity's frame of reference
	Fog_SetEyePosition (e->localmatrix);

	if (r_realtime_lighting->boolean)
	{
		d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &R_GetMeshTexture (e, hdr)->SRV);
		R_RenderAliasModel (e, hdr, &d3d_MeshDeferred, d3d_BlendNone, d3d_ZmodeNormal);
		return;
	}
	else
	{
		QTEXTURE *Luma = R_GetMeshLuma (e, hdr);

		R_LightAliasModel (e, hdr);
		d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &R_GetMeshTexture (e, hdr)->SRV);
		R_RenderAliasModel (e, hdr, &d3d_MeshNormalShader, d3d_BlendNone, d3d_ZmodeNormal);

		// add dynamic lights (this is the very same pass type as is used for surfaces)
		R_DrawDynamicLights (e);

		// now see do we need to add the luma pass
		if (Luma)
		{
			d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &Luma->SRV);
			R_RenderAliasModel (e, hdr, &d3d_MeshLumaShader, d3d_BlendMax, d3d_ZmodeNowrite);
		}
	}
}


void R_SetupStaticAliasModel (entity_t *e)
{
	// preliminary setup to store it's transforms/etc
	R_SetupAliasModel (e);

	// and get some lighting info so that we don't need to trace the BSP tree so much at runtime
	R_LightAliasModel (e, e->model->aliashdr);

	// set last so that it will skip over the building at runtime but not at load time
	e->effects |= EF_STATICENT;
}


void R_BruteForceAliasModel (entity_t *e, QSHADER *shader, ID3D10BlendState *blendmode, ID3D10DepthStencilState *depthmode)
{
	model_t *clmodel = e->model;
	aliashdr_t *hdr = clmodel->aliashdr;

	shader->Bind ();
	d3d_State->OMSetBlendState (blendmode);
	d3d_State->OMSetDepthState (depthmode);

	d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &R_GetMeshTexture (e, hdr)->SRV);

	// setup the cbuffer for this pass
	R_SetupObjectConstants (e, 1.0f);

	// common vertex setup
	d3d_MeshBuffers[e->model->BufferSetNum].SetupToDraw (hdr, e->prevpose, e->currpose);

	// and draw the pass
	d3d_Device->DrawIndexed (hdr->numindexes, 0, 0);
	c_draw_call++;
}


void R_DrawAliasLuma (entity_t *e)
{
	model_t *clmodel = e->model;
	aliashdr_t *hdr = clmodel->aliashdr;

	// signifies that we're going to a new mesh
	ref.AliasPasses++;

	QTEXTURE *Luma = R_GetMeshLuma (e, hdr);

	// now see do we need to add the luma pass
	if (Luma)
	{
		d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SURFACES, 1, &Luma->SRV);
		R_RenderAliasModel (e, hdr, &d3d_MeshLumaShader, d3d_BlendMax, d3d_ZmodeNowrite);
	}
}


void R_DrawAliasAdjacency (entity_t *e)
{
	model_t *clmodel = e->model;
	aliashdr_t *hdr = clmodel->aliashdr;

	// setup the cbuffer for this pass
	R_SetupObjectConstants (e, 1.0f);

	// common vertex setup
	d3d_MeshBuffers[e->model->BufferSetNum].SetupAdjacency (hdr, e->prevpose, e->currpose);

	d3d_Device->DrawIndexed (hdr->numadjacency, 0, 0);
	c_draw_call++;
}

